#include "kbc.h"



/** Initializes the KeyBoard Controller. If 'debug' is not 0 every
 * read or write to the KBC, together with the read or writed data is
 * echoed to the screen. In this mode the function name being executed
 * is printed followed by a ':', add=xx means the I/O address written,
 * and data=xx the data read or written.
 *
 *  A 1 is returned if a mouse is found.
 *
 * The function starts to flush any pending data in the KBC, reading
 * the data register until a timeout is received.
 *
 * Then it enables the KBC mouse interface, send a MouseReset to the
 * mouse and checks for the correct answer (0xFA, 0xAA then 0x00).  If
 * the mouse was detected, data streaming is disabled (StreamDisable),
 * SampleRate is set to 100, Resolution is set to 8 counts/mm, Scaling
 * is set to 1:1, the mouse interrupts generation are enabled at the
 * KBC (not at the PIC!), and data streaming enabled
 * (StreamEnable). All this (but StreamEnable) shouldn't be necessary,
 * as MouseReset is supposed to do it properly.
 *
 * The KBDRate is also set to its maximum speed.
 *
 * Uses write_kbc_cmd(), write_kbc_data(), read_kbc() and write_aux()
 * </pre></code>
 */


/** Disable the mouse, sending a StreamDisable to the mouse, then
 * disables generation of mouse interrupts at the KBC, sends a
 * DisableMouse to the KBC and finnaly disable mouse interrupts at the
 * PIC2
 */
void mouse_disable(void)
{
	write_aux(StreamDisable);
	
	write_kbc_cmd(ReadCommand);
	write_kbc_cmd_arg(WriteCommand, read_kbc() & ~(INT_12 | EN_MOUSE));
	
	write_kbc_cmd(DisableMouse);
	disable_irq(MOUSE_IRQ);
}


/** Write 'data' to the command register, returning 0 for OK or -1 on
 * timeout.
 * 
 * Uses write_kbc()
 */
int write_kbc_cmd(unsigned data)
{
	return write_kbc(CMD_REG, data);
}

int write_kbc_cmd_arg(unsigned data, unsigned arg)
{
	if(write_kbc_cmd(data)) return -1;
	return write_kbc_data(arg);
}


/** Write 'data' to the data register, returning ACK, Error or -1 on timeout.
 *
 * If a Resend reply is received from the KBC, it retries up to 3 times to
 * write the data.
 *
 * Uses write_kbc() and read_kbc()
 */
int write_kbc_data(unsigned data)
{
	int i, aux;
	for(i = 0; i < 3; i++)
	{
		write_kbc(DATA_REG, data);
		aux = read_kbc();
		if(aux == ACK || aux == Error || aux == -1)
			return aux;
		
		mili_sleep(1);
	}
	
	return -1;
}

int write_kbc_data_arg(unsigned data, unsigned arg)
{
	int resp = write_kbc_data(data);
	if(resp != ACK) return resp;
	return write_kbc_data(arg);
}


/** Send 'cmd' to the mouse, prefacing it with the WriteMouse command
 * to the command register.
 *
 * If a Resend reply is received from the KBC, it retries up to 3 times to
 * write 'cmd', resending the whole sequence.
 *
 * Uses write_kbc_cmd(), write_kbc() and read_kbc()
 */ 
int write_aux(unsigned cmd)
{
	int i, data;
	for(i = 0; i < 3; i++)
	{
		data = write_kbc_cmd_arg(WriteMouse, cmd);
		if(data == ACK || data == Error || data == -1)
			return data;
		
		mili_sleep(1);
	}
	
	return -1;
}

int write_aux_arg(unsigned cmd, unsigned arg)
{
	int resp = write_aux(cmd);
	if(resp != ACK) return resp;
	return write_aux(arg);
}


/** Returns the data read a the data register.
*
*  Waits for OBF on status register up to KBC_TIMEOUT milliseconds,
*  then reads and returns the data read at the data register. Returns
*  -1 on timeout or transmission error.
*/
int read_kbc(void)
{
	Byte stat, data;
	int timeout = msec + KBC_TIMEOUT;
	while(msec < timeout)
	{
		stat = inportb(STAT_REG);
		
		//loop until 8042 output buffer full
		if((stat & OBF) != 0)
		{
			data = inportb(DATA_REG);
			//or parity error or receive timeout
			if((stat & (PAR_ERR | TO_ERR)) == 0)
			{
				return data;
			}
			else
			{
				return -1;
			}
		}
		mili_sleep(1);
	}
	return -1;
}


/** Write 'data' at IO address 'adr' , returning -1 on error or timeout.
 *
 * Waits for IBF on status register , than writes data to 'adr',
 * return -1 on timeout.
 */
int write_kbc(unsigned adr, unsigned data)
{
	Byte stat;
	
	int timeout = msec + KBC_TIMEOUT;
	while(msec < timeout)
	{
		stat = inportb(STAT_REG);
		
		//loop until 8042 input buffer empty
		if((stat & IBF) == 0)
		{
			outportb(adr, data); //argumentos da fun��o
			return 0;
		}
		
		mili_sleep(1);
	}
	// terminou o tempo
	return -1;
}

